package com.dianping.localcache;

import java.util.LinkedList;
import java.util.ListIterator;

import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteStatement;
import android.os.Handler;
import android.os.Message;

import com.dennytech.tac.utils.Daemon;
import com.dennytech.tac.utils.Log;

public class ByteArrayCacheManager extends BaseCacheManager<byte[]> {

	private static final String TAG = ByteArrayCacheManager.class.getSimpleName();

	private static final String DB_TABLE = "bytearr";
	private static final String FD_URL = "K";
	private static final String FD_VAL = "V";
	private static final String[] _COLS = { FD_VAL };

	private SQLiteDatabase db;
	private SQLiteStatement deleteStat;
	private SQLiteStatement clearStat;
	private DatabaseUtils.InsertHelper insertHelper;
	private int iUrl, iVal;

	private final LinkedList<Operation> operations = new LinkedList<Operation>();
	private OpHandler handler;

	public ByteArrayCacheManager(SQLiteDatabase db, long lifetime,
			int cacheCapacity) {
		super(new CacheInfoManagerImpl(db, DB_TABLE + "_ci"), lifetime,
				cacheCapacity, false);
		this.db = db;

		StringBuilder sb = new StringBuilder();
		sb.append("CREATE TABLE IF NOT EXISTS ").append(DB_TABLE).append(" (");
		sb.append(FD_URL).append(" TEXT PRIMARY KEY, ");
		sb.append(FD_VAL).append(" TEXT);");

		try {
		    db.execSQL(sb.toString());

		    deleteStat = db.compileStatement("DELETE FROM " + DB_TABLE + " WHERE "
				+ FD_URL + "=?");
		    clearStat = db.compileStatement("DELETE FROM " + DB_TABLE + " WHERE 1");
		    insertHelper = new DatabaseUtils.InsertHelper(db, DB_TABLE);
			iUrl = insertHelper.getColumnIndex(FD_URL);
			iVal = insertHelper.getColumnIndex(FD_VAL);
		} catch (Exception e) {
			Log.e(TAG, e.getLocalizedMessage());
			this.db = null;
		}
	}

	@Override
	protected byte[] retrieveData(String url) {
		Log.i(TAG, "retrieveData with url = " + url);

		synchronized (operations) {
			ListIterator<Operation> itr = operations.listIterator();
			while (itr.hasPrevious()) {
				Operation o = itr.previous();
				if (url.equals(o.url)) {
					Log.i(TAG, "retrieveData hits in operations");
					return o.data;
				}
			}
		}

		if (db == null) {
			Log.i(TAG, "retrieveData return null because db = null");
			return null;
		}

		try {
			Cursor c = db.query(DB_TABLE, _COLS, FD_URL + "=\"" + url + "\"", null,
				null, null, null);
			byte[] bytes = null;
			if (c.moveToFirst()) {
				try {
				    bytes = c.getBlob(0);
				} catch (Exception e) {
					return null;
				}
			} else {
				Log.i(TAG, "retrieveData return null because c.moveToFirst fail");
			}
			c.close();
			return bytes;
		} catch (Exception e) {
			Log.e(TAG, e.getLocalizedMessage());
			return null;
		}
	}

	@Override
	protected int saveData(String url, byte[] data) {
		if (db == null)
			return 0;

		synchronized (operations) {
			Operation o = new Operation();
			o.url = url;
			o.data = data;
			operations.addLast(o);
		}
		if (handler == null)
			handler = new OpHandler();
		handler.sendEmptyMessage(0);
		return data.length;
	}

	@Override
	protected void deleteData(String url) {
		if (db == null)
			return;

		synchronized (operations) {
			Operation o = new Operation();
			o.url = url;
			o.data = null;
			operations.addLast(o);
		}
		if (handler == null)
			handler = new OpHandler();
		handler.sendEmptyMessage(0);
	}

	private class Operation {
		public String url;
		public byte[] data;
	}

	private class OpHandler extends Handler {
		public OpHandler() {
			super(Daemon.looper());
		}

		@Override
		public void handleMessage(Message msg) {
			if (db == null)
				return;

			Operation op = null;
			synchronized (operations) {
				if (operations.size() > 0) {
					op = operations.getFirst();
				}
			}
			if (op == null || op.url == null)
				return;
			try {
				if (op.data == null) {
					synchronized (deleteStat) {
						deleteStat.bindString(1, op.url);
						deleteStat.execute();
					}
				} else {
					synchronized (insertHelper) {
						insertHelper.prepareForInsert();
						insertHelper.bind(iUrl, op.url);
						insertHelper.bind(iVal, op.data);
						if (insertHelper.execute() < 0) {
							// failed, but i don't care
						} else {
							// succeed
						}
					}
				}
			} catch (SQLiteException e) {
			} catch (Error e) {
			} finally {
				synchronized (operations) {
					operations.remove(op);
				}
			}
		}
	}

	@Override
	public synchronized void clear() {
		cacheInfoManager.clear();
		clearStat.execute();
	}
}
